package ee.videorentalstore.protocols.commands.customercommands;

import java.util.List;
import ee.videorentalstore.UserAndProtocolManager;
import ee.videorentalstore.bonustracker.CustomerAndItsBonusPoints;
import ee.videorentalstore.bonustracker.CustomerBonusPointsManager;
import ee.videorentalstore.filmsrentedbyusers.FilmRentedByUser;
import ee.videorentalstore.filmsrentedbyusers.FilmsRentedByUsersManager;
import ee.videorentalstore.inventories.Film;
import ee.videorentalstore.inventories.FilmInventory;
import ee.videorentalstore.inventories.FilmType;
import ee.videorentalstore.protocols.commands.Command;
import ee.videorentalstore.users.User;
import ee.videorentalstore.users.UsersDatabase;
import ee.videorentalstore.users.types.Customer;
import ee.videorentalstore.utils.PriceCalculator;
import ee.videorentalstore.utils.UserInputReader;

public class RentAFilmByIDCommand extends Command
{
	private final int ID_OF_A_FILM_TO_RENT_POSITION_IN_SPLITTED_INSTRUCTIONS_LIST = 1;
	private final int NUMBER_OF_RENTAL_DAYS_POSITION_IN_SPLITTED_INSTRUCTIONS_LIST = 2;
	private final int BONUS_POINTS_GIVEN_PER_RENTAL = 1;
	private final int BONUS_POINTS_GIVEN_PER_RENTAL_BY_NEWLY_RELEASED_FILM = 2;
	private Film filmSelectedForRental;
	private User userCurrentlyInControl = UserAndProtocolManager.getInstance().getCurrentUserInControl();
	private Integer numberOfRentalDays;
	private final int ENOUGH_BONUS_POINTS_FOR_PAYING_A_RENTAL_DAY = 25;
	private boolean customerAgreedToPayWithBonusPoints = false;
	
	
	@Override
	public String getCommandDescription() 
	{
		return "RENT A FILM BY ID";
	}
	
	
	@Override
	public void executeCommand(String inputStreamInstructionsToProcess) 
	{
		List<String> splittedInstructionsList = 
				getSplittedInputStreamInstructionsToProcess(inputStreamInstructionsToProcess);
		Integer idOfAFilmToRent = getSpecificIntegerValueOutOfSplittedInstructionsList(
				splittedInstructionsList, ID_OF_A_FILM_TO_RENT_POSITION_IN_SPLITTED_INSTRUCTIONS_LIST);
		numberOfRentalDays = getSpecificIntegerValueOutOfSplittedInstructionsList(
				splittedInstructionsList, NUMBER_OF_RENTAL_DAYS_POSITION_IN_SPLITTED_INSTRUCTIONS_LIST);
		
		if (idOfAFilmToRent != null && numberOfRentalDays != null)
		{
			boolean selectedFilmExistsInInventoryAndIsAvailableForRental = 
					getWhetherFilmExistsInInventoryAndIsAvailableForRental(idOfAFilmToRent);
			if (selectedFilmExistsInInventoryAndIsAvailableForRental)
			{
				displayRentalTermsToUser();
				boolean customerAgreedWithOverallRentalTerms = getWhetherCustomerAgreedWithOverallRentalTerms();
				if (customerAgreedWithOverallRentalTerms)
				{
					insertRecordIntoFilmsRentedByUsers();
					updateCustomerBonusPoints();
					setFilmNotToBeAvailableInInventory();
					System.out.println("Film rental was successful!");
				}
				else
				{
					System.out.println("Film rental was cancelled! Type any command...");
				}
			}
		}
		else
		{
			System.out.println("Command could not be executed due to inserted instructions errors!");
		}	
	}
	
	
	private boolean getWhetherFilmExistsInInventoryAndIsAvailableForRental(
			int idOfAFilmToRent)
	{
		filmSelectedForRental = FilmInventory.getInstance().getFilmByID(idOfAFilmToRent);
		boolean filmExistsInInventory = (filmSelectedForRental != null);
		
		if (filmExistsInInventory)
		{
			if (FilmInventory.getInstance().getAllAvailableFilmsInInventory().contains(filmSelectedForRental))
			{
				return true;
			}
			else
			{
				System.out.println("Film is not available. It has been probably rented out already!");
				return false;
			}
		}
		else
		{
			System.out.println("Film with given id does not exist!");
			return false;
		}
	}
	
	
	private void displayRentalTermsToUser()
	{
		System.out.println("You have selected to rent\n" +
				filmSelectedForRental.getName() + " (" + filmSelectedForRental.getType().toString() + ") " +
				"for " + numberOfRentalDays + " days, with a cost of: " 
				+ PriceCalculator.calculateRentalFee(numberOfRentalDays, filmSelectedForRental.getType()) + "\n\n");
		
		handleBonusPointsOperationsForNewlyReleasedFilm();
		
		System.out.println("Will you agree with given terms? Type YES if you agree, type NO when otherwise...");
	}
	
	
	private void handleBonusPointsOperationsForNewlyReleasedFilm()
	{
		boolean customerHasGotEnoughBonusPointsToPayForANewlyReleasedFilmRentalPeriod = 
				hasCustomerGotEnoughBonusPointsToPayForANewlyReleasedFilmRentalPeriod();
		if (filmSelectedForRental.getType() == FilmType.NEW_RELEASE
				&& customerHasGotEnoughBonusPointsToPayForANewlyReleasedFilmRentalPeriod)
		{
			System.out.println("You have " + getCustomerBonusPointsWhoIsCurrentlyInControl() + " bonus points, \n" +
					"which is enough to pay for your rental.\n " +
					"Would you like to use them? Type YES to use bonus points, type NO when otherwise...");
			
			
			String acceptUsingBonusPoints = UserInputReader.getInstance().readLine();
			
			if (acceptUsingBonusPoints.equalsIgnoreCase("YES"))
			{
				System.out.println("\nYou have selected to rent " +
						filmSelectedForRental.getName() + " (" + filmSelectedForRental.getType().toString() + ") " +
						"for " + numberOfRentalDays + " days " +
						"(Paid with " + getAmountOfBonusPointsToSubtractWhenRentingAGivenNewlyReleasedFilm() + " bonus points)\n" +
						"Remaining bonus points: " + 
						getAmountOfBonusPointsThatRemainAfterRentingAGivenNewlyReleasedFilmAndPayingWithBonusPoints() + "\n\n");
				
				customerAgreedToPayWithBonusPoints = true;
			}
			else
			{
				customerAgreedToPayWithBonusPoints = false;
			}
		}
	}
	
	
	private boolean hasCustomerGotEnoughBonusPointsToPayForANewlyReleasedFilmRentalPeriod()
	{
		int customerBonusPoints = getCustomerBonusPointsWhoIsCurrentlyInControl();
		int bonusPointsEquivalenceWithDays = customerBonusPoints/ENOUGH_BONUS_POINTS_FOR_PAYING_A_RENTAL_DAY;
		if (bonusPointsEquivalenceWithDays >= numberOfRentalDays)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	
	private int getCustomerBonusPointsWhoIsCurrentlyInControl()
	{
		Customer customerCurrentlyInControl = UsersDatabase.getInstance().getCustomer(
				userCurrentlyInControl.getUserID());
		CustomerAndItsBonusPoints customerAndItsBonusPoints = 
				CustomerBonusPointsManager.getInstance().getCustomerAndItsBonusPoints(customerCurrentlyInControl);
		int customerBonusPoints = customerAndItsBonusPoints.getBonusPoints();
		return customerBonusPoints;
	}
	
	
	private int getAmountOfBonusPointsToSubtractWhenRentingAGivenNewlyReleasedFilm()
	{
		return numberOfRentalDays * ENOUGH_BONUS_POINTS_FOR_PAYING_A_RENTAL_DAY;
	}
	
	
	private int getAmountOfBonusPointsThatRemainAfterRentingAGivenNewlyReleasedFilmAndPayingWithBonusPoints()
	{
		int allBonusPointsThatCustomerHas = getCustomerBonusPointsWhoIsCurrentlyInControl();
		return allBonusPointsThatCustomerHas - getAmountOfBonusPointsToSubtractWhenRentingAGivenNewlyReleasedFilm();
	}
	
	
	private boolean getWhetherCustomerAgreedWithOverallRentalTerms()
	{
		String acceptOverallRentalTerms = UserInputReader.getInstance().readLine();
		
		if (acceptOverallRentalTerms.equalsIgnoreCase("YES"))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	
	private void insertRecordIntoFilmsRentedByUsers()
	{
		FilmRentedByUser filmRentedByUser = new FilmRentedByUser();
		filmRentedByUser.setFilm(filmSelectedForRental);
		filmRentedByUser.setUser(userCurrentlyInControl);
		filmRentedByUser.setNumberOfRentalDays(numberOfRentalDays);
		if (customerAgreedToPayWithBonusPoints)
		{
			filmRentedByUser.setIsPaidWithBonusPoints(true);
		}
		
		FilmsRentedByUsersManager.getInstance().addFilmRentedByUser(filmRentedByUser);
	}

	
	private void updateCustomerBonusPoints()
	{
		Customer customerCurrentlyInControl = UsersDatabase.getInstance().getCustomer(
				userCurrentlyInControl.getUserID());
		CustomerAndItsBonusPoints customerBonusPoints = 
				CustomerBonusPointsManager.getInstance().getCustomerAndItsBonusPoints(customerCurrentlyInControl);
		if (filmSelectedForRental.getType() == FilmType.NEW_RELEASE
				&& customerAgreedToPayWithBonusPoints)
		{
			customerBonusPoints.setBonusPoints(
					getAmountOfBonusPointsThatRemainAfterRentingAGivenNewlyReleasedFilmAndPayingWithBonusPoints()
					+ getHowMuchToIncrementCustomerBonusPointsDueToRentalMade());
		}
		else
		{
			int bonusPointsAtTheBeginning = customerBonusPoints.getBonusPoints();
			customerBonusPoints.setBonusPoints(bonusPointsAtTheBeginning 
					+ getHowMuchToIncrementCustomerBonusPointsDueToRentalMade());
		}
	}
	
	
	private int getHowMuchToIncrementCustomerBonusPointsDueToRentalMade()
	{
		if (filmSelectedForRental.getType() == FilmType.NEW_RELEASE)
		{
			return BONUS_POINTS_GIVEN_PER_RENTAL_BY_NEWLY_RELEASED_FILM;
		}
		else
		{
			return BONUS_POINTS_GIVEN_PER_RENTAL;
		}
	}
	
	
	private void setFilmNotToBeAvailableInInventory()
	{
		FilmInventory.getInstance().setFilmHasBeenRentedOut(filmSelectedForRental);
	}
}
